/************************************************
 * MattiseOS Kernel Drivers
 * RamFS Driver
 * Copyright 2008 Matthew Iselin
 ***********************************************/

// Includes
#include <mattise.h>
#include <stdint.h>
#include <string.h>
#include <mem/util.h>
#include <kernio/io.h>
#include <devman/devman.h>

// Defines

// seeking stuff
#define SEEK_SET		0
#define SEEK_CUR		1
#define SEEK_END		2

// Prototypes

#define DEBUG 0

#if DEBUG
	#define DEBUG_PRINT (dprintf)
#else
	#define DEBUG_PRINT(a,...) 
#endif

// a local descriptor
struct ramfs_fd
{
	int avail;
	unsigned int isfile;
	unsigned int memoffset;
	unsigned int maxsz;
	unsigned int offset;
};
volatile struct ramfs_fd desctable[512];

// ramdisk stuff

struct ramdisk_head {
	unsigned int total_size; // total ramdisk data section size
	unsigned int numfiles; // total number of files
	unsigned int firstfile; // location of the first file (in data area)
	unsigned int filetab; // location of the file table
};

struct ramdisk_filent {
	char name[256]; // name of the file
	unsigned int offset; // offset in the binary
	unsigned int size; // size of the file
};

struct dirent {
	char			d_name[1024]; // the filename
	unsigned long	d_off; // offset into the directory on the FS
	unsigned short	d_namlen; // length of the filename in d_name
	unsigned short	d_reclen; // unknown
	unsigned short	d_ino; // file serial number
} __attribute__((packed));

// Variables

// the base address of the ramfs binary
unsigned int base = 0;

// Functions

// opens a file
int ramfs_open( const char* path, int UNUSED(flags) )
{
	DEBUG_PRINT("ramfs_open [%x]\n", base);

	// find a space in the ramfs_fd table
	int i;
	for( i = 0; i < 512; i++ )
		if( desctable[i].avail )
			break;

	// not successful?
	if( i == 512 )
		return -1;

	// are we opening just the folder?
	if(strncmp(path, "/ramfs", strlen(path)) == 0 || strncmp(path, "/ramfs/", strlen(path)) == 0)
	{
		// good to go
		desctable[i].avail = 0;
		desctable[i].memoffset = 0;
		desctable[i].maxsz = 0;
		desctable[i].offset = 0;
		desctable[i].isfile = 0;
		return i;
	}

	// grab the file table
	struct ramdisk_head* header = (struct ramdisk_head*) base;
	DEBUG_PRINT("%d files\n", header->numfiles);
	struct ramdisk_filent* ftab = (struct ramdisk_filent*) (base + header->filetab);

	// search for the file
	uint32_t z;
	for( z = 0; z < header->numfiles; z++ )
	{
		// is it the same?
		DEBUG_PRINT("checking %s against %s\n", ftab[z].name, path);
		if( strncmp( path + strlen( "/ramfs/" ) /* skip the "/ramfs/" */, ftab[z].name, strlen( path + strlen( "/ramfs/" ) ) ) == 0 )
			break;
	}

	// was it not found?
	if( z == header->numfiles )
		return -1;

	// set it as unavailable
	desctable[i].avail = 0;
	desctable[i].memoffset = base + ftab[z].offset;
	desctable[i].maxsz = ftab[z].size;
	desctable[i].offset = 0;
	desctable[i].isfile = 1;

	// return the descriptor
	return i;
}

// reads from a file
unsigned long ramfs_read( int ramfs_fd, void* buff, unsigned long count )
{
	// verify the descriptor
	if( ramfs_fd < 0 || ramfs_fd >= 512 )
		return 0;

	// is it a folder?
	if(desctable[ramfs_fd].isfile == 0)
	{
		// grab the file table
		struct ramdisk_head* header = (struct ramdisk_head*) base;
		struct ramdisk_filent* ftab = (struct ramdisk_filent*) (base + header->filetab);

		// iterate!
		uint32_t z;
		for( z = 0; z < header->numfiles; z++ )
		{
			if(z == desctable[ramfs_fd].offset)
			{
				static struct dirent dir;
				strcpy(dir.d_name, ftab[z].name);
				dir.d_namlen = strlen(dir.d_name);
				dir.d_off = z;
				dir.d_ino = z;
				dir.d_reclen = sizeof(struct dirent);
				memcpy(buff, &dir, sizeof(struct dirent));
				desctable[ramfs_fd].offset++;
				return sizeof(struct dirent);
			}
		}

		// none found!
		return 0;
	}

	// truncate the count if needed
	if( count > desctable[ramfs_fd].maxsz )
		count = desctable[ramfs_fd].maxsz;
	if( (count + desctable[ramfs_fd].offset) > desctable[ramfs_fd].maxsz )
		count = desctable[ramfs_fd].offset - count;
	
	// zero count?
	if( count == 0 )
		return 0; // no data read

	// read in the data
	memcpy(buff, (void*) (desctable[ramfs_fd].memoffset + desctable[ramfs_fd].offset), count);
	desctable[ramfs_fd].offset += count;

	// return the number of bytes read
	return count;
}

// seeks within the file
unsigned long ramfs_seek( int ramfs_fd, unsigned long off, int set )
{
	// verify the descriptor
	if( ramfs_fd < 0 || ramfs_fd >= 512 )
		return 0;
	
	DEBUG_PRINT( "ramfs_seek\n" );
	
	// what's the set?
	switch( set )
	{
		case SEEK_SET:
			if( off > desctable[ramfs_fd].maxsz )
				off = desctable[ramfs_fd].maxsz;
			desctable[ramfs_fd].offset = off;
			break;
		case SEEK_CUR:
			desctable[ramfs_fd].offset += off;
			if( desctable[ramfs_fd].offset > desctable[ramfs_fd].maxsz )
				desctable[ramfs_fd].offset = desctable[ramfs_fd].maxsz;
			break;
		case SEEK_END:
			desctable[ramfs_fd].offset = desctable[ramfs_fd].maxsz - off;
			if( off > desctable[ramfs_fd].maxsz )
				desctable[ramfs_fd].offset = desctable[ramfs_fd].memoffset;
			break;
	}
	
	// return the new offset
	return desctable[ramfs_fd].offset;
}

// controls the driver itself (in this driver anyway)
int ramfs_ioctl( int ramfs_fd, int action, int data )
{
	// always allow setting the base (the ramfs is only used at startup, before malicious programs are loaded)
	if( action == 1 && ramfs_fd == 512 )
	{
		base = data;
		DEBUG_PRINT("set!\n");
	}
	return 0;
}

// gets information about a specific open descriptor
int ramfs_stat( int ramfs_fd, struct stat* buf )
{
	// verify the descriptor
	if( ramfs_fd < 0 || ramfs_fd >= 512 )
		return -1;
	
	// yes, fill the buffer (some things get filled by the caller, we fill what we can)
	buf->st_size = desctable[ramfs_fd].maxsz;
	buf->st_blocks = buf->st_size;

	// success, no failure
	return 0;
}

// closes a descriptor
int ramfs_close( int ramfs_fd )
{
	// verify the descriptor
	if( ramfs_fd < 0 || ramfs_fd >= 512 )
		return -1;
	
	// close the file (basically make it available again)
	desctable[ramfs_fd].avail = 1;

	// success
	return 0;
}

void ramfs_install( uint32_t addr )
{
	// set the base
	base = addr;

	// setup the descriptor table
	int i;
	for( i = 0; i < 512; i++ )
		desctable[i].avail = 1;

	// tell me what's going on
	DEBUG_PRINT("Installing ramfs, base=%x\n", addr);

	// install the device calls
	InstallDevice( "/ramfs", (unsigned int) ramfs_open, (unsigned int) ramfs_read, 0, (unsigned int) ramfs_seek, (unsigned int) ramfs_ioctl, (unsigned int) ramfs_stat, (unsigned int) ramfs_close, 1 );
}
